<?php
/**
 * This file defines the {@see \BF\Net\MailAddress} class.
 *
 * @author     Kado <ulfikado@gmail.com>
 * @category   Beluga PHP5.3+ Framework
 * @package    BF\Net
 * @since      2015-01-31 00:29
 * @subpackage Core
 * @version    0.1
 */

namespace BF\Net
{

    /**
     * Diese Klasse definiert ein Objekt das alle Informationen zu eine E-Mail
     * Adresse kapselt.
     *
     * @since      v0.1
     */
    class MailAddress
    {

      # <editor-fold defaultstate="collapsed" desc=" - - -   P R O T E C T E D   F I E L D S   - - - - - - - - - - - - - - - - - - - -">

      /**
       * The mail address user part (every thing before the first @)
       *
       * @var string
       */
      protected $_userPart;

      /**
       * The domain part of the mail address.
       *
       * @var \BF\Net\Domain
       */
      protected $_domainPart;

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - >   P R I V A T E   C O N S T R U C T O R   - - - - - - - - - - - - - - - - -">

      /**
       * Inits a new instance.
       *
       * @param string         $userPart   The mail address user part (every thing before the first @)
       * @param \BF\Net\Domain $domainPart The domain part of the mail address.
       */
      private function __construct( $userPart, \BF\Net\Domain $domainPart )
      {
         $this->_userPart   = $userPart;
         $this->_domainPart = $domainPart;
      }

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - -   P U B L I C   M E T H O D S   - - - - - - - - - - - - - - - - - - - - - -">

       /**
        * Magic getter.
        *
        * @param  string $name
        * @return mixed
        */
       public function __get( $name )
      {
         switch ( \strtolower( $name ) )
         {
            case 'user':
            case 'userpart':
               return $this->_userPart;
            case 'domain':
            case 'domainpart':
               return $this->_domainPart;
            default:
               return false;
         }
      }

      /**
       * Returns the mail address user part (every thing before the first @)
       *
       * @return string
       */
      public function getUserPart()
      {
          return $this->_userPart;
      }

      /**
       * Returns the domain part of the mail address.
       *
       * @return \BF\Net\Domain
       */
      public function getDomainPart()
      {
          return $this->_domainPart;
      }

      /**
       * Magic method to support casting to string.
       *
       * @return string
       */
      public function __toString()
      {
         return $this->_userPart . '@' . $this->_domainPart->toString();
      }

      /**
       * Checks if the defined value is equal to current mail address value.
       *
       * @param  mixed   $value  The value to check against.
       * @param  boolean $strict Can only be equal if value is of type {@see \BF\Net\MailAddress}
       * @return boolean
       */
      public function equals( $value, $strict = false )
      {
         if ( \is_null( $value ) )
         {
            return false;
         }
         if ( $value instanceof \BF\Net\MailAddress )
         {
            return
               ( $value->_userPart == $this->_userPart )
               &&
               ( ( (string) $value->_domainPart ) === ( (string) $this->_domainPart ) );
         }
         if ( $strict )
         {
            return false;
         }
         $val = null;
         if ( \is_string( $value ) )
         {
            if ( false !== ( $val = \BF\Net\MailAddress::Parse( $value ) ) )
            {
               return
                  ( $val->_userPart === $this->_userPart )
                  &&
                  ( ( (string) $val->_domainPart ) === ( (string) $this->_domainPart ) );
            }
            return false;
         }
         if ( $value instanceof \BF\Net\Domain )
         {
            return ( ( (string) $value ) === ( (string) $this->_domainPart ) );
         }
         $typeInfo = new \BF\Type( $value );
         if ( ! $typeInfo->hasAssociatedString() )
         {
            return false;
         }
         if ( false === ($val = \BF\Net\MailAddress::Parse( $typeInfo->getStringValue(), false, false, true ) ) )
         {
            return false;
         }
         return
            ( $val->_userPart === $this->_userPart )
            &&
            ( ( (string) $val->_domainPart ) === ( (string) $this->_domainPart ) );
      }

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - -   P U B L I C   S T A T I C   M E T H O D S   - - - - - - - - - - - - - - -">

      /**
       * Initialisiert eine neue -Instanz.alisiert die Daten einer
       * Objektinstanz neu aus der übergebenen Zeichenkette.
       *
       * Diese Methode muß mit den Daten umgehen können die von __toString()
       * zurueck gegeben werden.
       *
       * @param  string  $mailAddressString
       * @param  boolean $requireTLD
       * @param  boolean $requireKnownTLD
       * @param  boolean $allowReseved
       * @return boolean
       */
      public static function Parse(
         $mailAddressString, $requireTLD = true, $requireKnownTLD = true, $allowReseved = false )
      {

         if ( ! \is_string( $mailAddressString ) )
         {
            // If $mailAddressString is not a string, dont handle it…
            return false;
         }

         if ( false === ( $firstAtIndex = \BF\strpos( $mailAddressString, '@' ) ) )
         {
            // If $mailAddressString doent contain the @ char, parsing fails
            return false;
         }

         // Get the user part string
         $user   = \strtolower( \substr( $mailAddressString, 0, $firstAtIndex ) );
         // Get the domain part string
         $domain = \strtolower( \substr( $mailAddressString, $firstAtIndex + 1 ) );

         if ( ! \preg_match( '~^[a-z_][a-z0-9_.%-]*$~i', $user ) )
         {
            // If the userpart uses invalid characters, parsing fails
            return false;
         }

         if ( false === ( $_domain = \BF\Net\Domain::Parse( $domain, $requireTLD && $requireKnownTLD ) ) )
         {
            // If the domain part is initially wrong, parsing fails
            return false;
         }

         if ( $requireTLD && ! $_domain->HasTLD )
         {
            // If a TLD is required but not defined, parsing fails
            return false;
         }

         if ( ! $allowReseved && $_domain->IsReserved )
         {
            // If the domain part points to a reserved domain name or TLD, parsing fails if this is forbidden
            return false;
         }

         // All is fine, return the resulting MailAddress instance.
         return new \BF\Net\MailAddress( $user, $_domain );

      }

      /**
       * Extracts all mailadresses from inside the defined $string.
       *
       * @param  string $string The string to parse.
       * @return \BF\Net\MailAddress[] Return the found mail addresses as a \BF\Net\MailAddress array.
       */
      public static function ExctractAllFromString( $string )
      {

         // Init the resulting addresses array
         $addresses = array();
         $matches   = null;

         // Find some rough mail address definitions
         if ( ! \preg_match_all( '~[a-z0-9%_.-]+@[]+[a-z0-9_.-]+~i', $string, $matches ) )
         {
            return $addresses;
         }

         foreach ( $matches[ 0 ] as $match )
         {
            if ( false === ( $addr = \BF\Net\MailAddress::Parse( $match, false, false, true ) ) )
            {
               continue;
            }
            $addresses[] = $addr;
         }

         return $addresses;

      }

      # </editor-fold>

    }

}

